package org.jvm.rtda.heap.stringpool;

import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classLoader.*;
import org.jvm.util.JvmUtil;

import java.util.*;

/**
 * 字符串池
 *
 * @author 海燕
 * @date 2023/2/18
 */
public class StringPool {

    /**
     * 字符串池
     * key-> 字符串字面量
     * value -> java字符串实例
     */
    public static final Map<String, Object> internedStrings = new HashMap<>();

    /**
     * 根据字符串字面量，构造一个Java 字符串实例
     * 先检查字符串池，如果字符串池中已经存在字符串实例则直接返回
     *
     * @param jvmString 字符串字面量
     * @return
     */
    public static Object jString(String jvmString) {
        if (internedStrings.containsKey(jvmString)) {
            return internedStrings.get(jvmString);
        }
        Object javaStringObject = jvmStringToJavaString(KlassLoaderRegister.getBootKlassLoader(), jvmString);
        //创建好的字符串放入字符串池
        internedStrings.put(jvmString, javaStringObject);
        return javaStringObject;
    }

    /**
     * 将jvm中使用的字符串转换为java的String实例
     *
     * @param jvmString
     * @return
     */
    private static Object jvmStringToJavaString(BootKlassLoader classLoader, String jvmString) {
        //java字符串是java/lang/String类的一个实例，这个实例持有一个char数组，字段名为value
        Klass javaStringClass = classLoader.loadKlass("java/lang/String");
        Object javaStringObject = javaStringClass.newObject();
        //先建造一个char数组实例
        Klass charArrayClass = classLoader.loadKlass("[C");
        Object charArrayObject = charArrayClass.newArray(jvmString.length());
        char[] chars = charArrayObject.chars();
        for (int i = 0; i < chars.length; i++) {
            chars[i] = jvmString.toCharArray()[i];
        }
        //将char数组实例填充java String实例
        javaStringObject.setRefVar("value", "[C", charArrayObject);
        return javaStringObject;
    }


    /**
     * 检查javaString实例是否入池，如果实例在池中有相同字面含义的实例则返回池中已存在的实例（实质上是替换了入参的实例）。
     * 如果实例在池中没有相同字面含义的实例，则将实例入池
     *
     * @param javaString
     * @return
     */
    public static Object inter(Object javaString) {
        String jvmString = JvmUtil.javaStringToJvmString(javaString);
        if (internedStrings.containsKey(jvmString)) {
            return internedStrings.get(jvmString);
        }
        internedStrings.put(jvmString, javaString);
        return javaString;
    }
}
